/**
 * @class 相对变换器组件
 * @author DuskyHuang 圣子
 * @description 根据视角fov、方向和观察点变换节点的相对世界变换
*/

import { _decorator, Component, Node, Vec3 } from 'cc';
import { core, Notifications } from 'cfw';
import { LAYER_GIZMOS, Res, SceneGraph } from 'const';
import { Dsm, ICelestialSpecific } from 'prefabs';
import { createNodeAt } from 'helpers';
import { cosmos, CosmosProxy, gizmos, visit } from 'proxies';
import { Task } from 'notifications';
const { ccclass, disallowMultiple } = _decorator;

@ccclass('cocosmos.RelativeScalar')
@disallowMultiple(true)
export class RelativeScalar extends Component {

    protected onLoad(): void { this._init()._listen(); }

    protected onDestroy(): void { core.unload(this); }

    protected update(): void {
        if (this._scope) this._scope.active = gizmos.showScopes;
    }

    protected onTick(dt?: time.sec, tick?: num.int): void {
        this._updateTransform();
        this._checkPreloadTask();
    }

    private _init(): this {
        const key = this.node.name.replace(' ', '');
        this._scopeArgs = CosmosProxy.Scopes[key];
        if (!this._scopeArgs) console.warn("scalar not found:", key);
        if (this._scopeArgs[3]) {
            this._scope = createNodeAt('Scope', LAYER_GIZMOS,
                this.node, core.res.getPrefab(Res.Prefabs.SCOPE));
        }
        this._stellar = this.node.parent?.getChildByName(STELLAR);
        return this;
    }

    private _listen(): void {
        core.on(Notifications.Time.TICK, this.onTick, this, -1);
    }

    private _checkPreloadTask(): void {
        if (this._loadTexTaskLaunched) return;
        if ((cosmos.pow10 + PRELOAD_TOLERANCE) > this._scopeArgs[0]) {
            this._loadTexTaskLaunched = true;
            core.notify(Task.Load.APPROACH_SCOPE, this.node.name);
        }
    }

    private _updateTransform(): void {
        const [from, to, real] = this._scopeArgs, { pow10 } = cosmos;
        const activated = this._handleSpecialActive(from < pow10 && pow10 < to);
        // const activated = true; // BLACKHOLE
        this.node.active = activated;
        if (activated) {
            this._updateScale(real - pow10);
            this._updatePosition();
        }
    }

    private _handleSpecialScale(scale: number): number {
        return this.node.name === STELLAR ? Math.min(1, scale) : scale;
    }

    private _handleSpecialActive(active: boolean): boolean {
        const { inSolar, inStellar } = visit, { name } = this.node;
        if (inSolar && name === SPECIFIC) return false;
        else if (inStellar && [HELIO, SOLAR, PLANETARY].contains(name)) return false;
        else return active;
    }

    private _updateScale(exp: number): void {
        const scale = this._handleSpecialScale(10 ** exp);
        this.node.setWorldScale(scale, scale, scale);
    }

    private _updatePosition(): void {
        const { center, inPlanetary, inSatellite, inSolar, inStellar } = visit;
        if (!center) return;
        const { worldScale: { x: myScale }, name } = this.node;
        switch (true) {
            case inSatellite: {
                if (name === PLANETARY) {
                    Vec3.multiplyScalar(v3a, center.node.position, -myScale);
                    this.node.setWorldPosition(v3a);
                } else if (name === SOLAR) {
                    const { belong } = center as Dsm;
                    Vec3.multiplyScalar(v3a, belong.node.position, -myScale);
                    Vec3.scaleAndAdd(v3a, v3a, center.node.position, -myScale * POW_FACTOR_PS);
                    this.node.setWorldPosition(v3a);
                } else {
                    this.node.setWorldPosition(Vec3.ZERO);
                }
            } break;
            case inPlanetary: {
                if (name === PLANETARY) {
                    this.node.setWorldPosition(Vec3.ZERO);
                } else if (name === SOLAR) {
                    this.node.setWorldPosition(Vec3.multiplyScalar(v3a, center.node.position, -myScale));
                } else {
                    this.node.setWorldPosition(Vec3.ZERO);
                }
            } break;
            case inSolar: {
                this.node.setWorldPosition(Vec3.ZERO);
            } break;
            case inStellar: {
                const { stellar } = visit.transInfo;
                const { refer } = center as unknown as ICelestialSpecific;
                let Ref = refer;
                // Issue:01 Hack temp存在一定是SPC_2_SPC, Facing结束temp=null
                // 则在Facing的1秒钟内保持refer是from
                if (stellar?.refers?.temp && stellar?.refers?.from) {
                    Ref = stellar?.refers?.from;
                }
                if (!!Ref && name === STELLAR) {
                    // console.log(center.name, Ref.name);
                    const _scale = this._stellar?.worldScale?.x ?? 1;
                    this.node.setWorldPosition(Vec3.multiplyScalar(v3a, Ref.position, -_scale));
                } else {
                    this.node.setWorldPosition(Vec3.ZERO);
                }
            } break;
            default: this.node.setWorldPosition(Vec3.ZERO);
        }
        // FORMER RESERVED!
        // if (name === SOLAR || name == PLANETARY) {
        //     if (inPlanetary) {
        //         this.node.setWorldPosition(name === SOLAR ?
        //             Vec3.multiplyScalar(v3a, center.node.position, -scale) : Vec3.ZERO);
        //     } else if (inSatellite) {
        //         if (name === SOLAR) {
        //             const { belong } = center as Dsm;
        //             Vec3.multiplyScalar(v3a, belong.node.position, -scale);
        //             Vec3.scaleAndAdd(v3a, v3a, center.node.position, -scale * POW_FACTOR);
        //         } else {
        //             Vec3.multiplyScalar(v3a, center.node.position, -scale)
        //         }
        //         this.node.setWorldPosition(v3a);
        //     } else {
        //         this.node.setWorldPosition(Vec3.ZERO);
        //     }
        // } else if (name === STELLAR) {
        //     const { stellar } = visit.transInfo;
        //     const { refer, scalar } = center as unknown as ICelestialSpecific;
        //     if (!!refer) {
        //         this.node.setWorldPosition(Vec3.multiplyScalar(v3a, refer.position, -scale));
        //     } else this.node.setWorldPosition(Vec3.ZERO);
        // } else {
        //     this.node.setWorldPosition(Vec3.ZERO);
        // }
    }

    private _scopeArgs: scope;
    private _scope: Node;
    private _loadTexTaskLaunched: boolean = false;
    private _stellar: Node = null;
}

const { STELLAR, SOLAR, PLANETARY, SPECIFIC, HELIO, MILKY, LOCAL } = SceneGraph;
const { Solar, Planetary, Stellar } = CosmosProxy.Scopes;
const POW_FACTOR_PS = 10 ** (Planetary[2] - Solar[2]);
const POW_FACTOR_PSS = 10 ** (Planetary[2] - Stellar[2]);
const POW_FACTOR_SS = 10 ** (Solar[2] - Stellar[2]);
const v3a = new Vec3;
const PRELOAD_TOLERANCE: pow10 = 0.5;